package com.smartinput.websocket

import com.fasterxml.jackson.databind.ObjectMapper
import com.smartinput.device.application.service.DeviceService
import com.smartinput.device.domain.repository.DeviceRepository
import com.smartinput.dto.WebSocketMessageDto
import com.smartinput.voice.application.service.VoiceRecognitionService
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import org.springframework.web.socket.CloseStatus
import org.springframework.web.socket.TextMessage
import org.springframework.web.socket.WebSocketSession
import org.springframework.web.socket.handler.TextWebSocketHandler
import java.util.concurrent.ConcurrentHashMap

@Component
class SmartInputWebSocketHandler : TextWebSocketHandler() {
    private val logger = LoggerFactory.getLogger(javaClass)
    private val deviceSessions = ConcurrentHashMap<String, WebSocketSession>()
    private val userSessions = ConcurrentHashMap<String, MutableSet<WebSocketSession>>()
    private val objectMapper = ObjectMapper()

    @Autowired
    private lateinit var deviceService: DeviceService

    @Autowired
    private lateinit var voiceRecognitionService: VoiceRecognitionService

    @Autowired
    private lateinit var deviceRepository: DeviceRepository
    
    @Autowired
    private lateinit var deviceStatusManager: com.smartinput.device.application.service.DeviceStatusManager

    override fun afterConnectionEstablished(session: WebSocketSession) {
        var userId = session.attributes["userId"] as? String

        // 如果从认证拦截器中没有获取到 deviceId，尝试从查询参数中提取
        var deviceId = fetchPara("deviceId", session)

        if (deviceId != null) {
            deviceSessions[deviceId] = session

            if (userId != null) {
                userSessions.computeIfAbsent(userId) { mutableSetOf<WebSocketSession>() }.add(session)
            }

            // 确保设备存在于数据库中
            ensureDeviceExists(deviceId, userId)

            // 更新设备在线状态
            deviceService.updateDeviceStatus(deviceId, true, session.id)
            deviceStatusManager.updateWebSocketStatus(deviceId, true)

            // 发送连接确认
            sendMessage(
                session, WebSocketMessageDto(
                    type = "connection_established",
                    data = mapOf("deviceId" to deviceId, "timestamp" to System.currentTimeMillis())
                )
            )

            logger.info("WebSocket连接建立: deviceId=$deviceId, userId=$userId")
        } else {
            logger.warn("WebSocket连接建立失败：无法获取deviceId")
            session.close()
        }
    }

    private fun fetchPara(paraName: String, session: WebSocketSession): String? {
        var paraValue = session.attributes[paraName] as? String
        if (paraValue == null) {
            val query = session.uri?.query
            if (query != null) {
                val params = query.split("&").associate { param ->
                    val parts = param.split("=", limit = 2)
                    if (parts.size == 2) parts[0] to parts[1] else parts[0] to ""
                }
                paraValue = params[paraName]
                if (paraValue != null) {
                    session.attributes[paraName] = paraValue
                    logger.info("从查询参数提取$paraName: $paraValue")
                }
            }
        }

        // 如果查询参数中没有，尝试从 URL 路径中提取
        if (paraValue == null) {
            val path = session.uri?.path
            if (path != null && path.startsWith("/ws/")) {
                paraValue = path.substring(4) // 移除 "/ws/" 前缀
                session.attributes[paraName] = paraValue
                logger.info("从URL路径提取$paraName: $paraValue")
            }
        }
        return paraValue
    }

    override fun handleTextMessage(session: WebSocketSession, message: TextMessage) {
        try {
            val webSocketMessage = objectMapper.readValue(message.payload, WebSocketMessageDto::class.java)

            when (webSocketMessage.type) {
                "text_push" -> handleTextPush(session, webSocketMessage)
                "mobile_text_input" -> handleMobileTextInput(session, webSocketMessage)
                "voice_stream" -> handleVoiceStream(session, webSocketMessage)
                "heartbeat", "device_heartbeat" -> handleHeartbeat(session, webSocketMessage)
                "device_status_request" -> handleStatusRequest(session, webSocketMessage)
                "auth" -> handleAuth(session, webSocketMessage)
                "connection_confirm" -> handleConnectionConfirm(session, webSocketMessage)
                else -> logger.warn("未知的消息类型: ${webSocketMessage.type}")
            }
        } catch (e: Exception) {
            logger.error("处理WebSocket消息时出错", e)
            sendError(session, "消息格式错误")
        }
    }

    override fun afterConnectionClosed(session: WebSocketSession, status: CloseStatus) {
        val deviceId = session.attributes["deviceId"] as? String
        val userId = session.attributes["userId"] as? String

        if (deviceId != null) {
            deviceSessions.remove(deviceId)
            // 更新设备状态管理器
            deviceStatusManager.updateWebSocketStatus(deviceId, false)
            deviceStatusManager.removeDeviceStatus(deviceId)
            
            // 安全地更新设备状态，使用异步处理避免阻塞
            try {
                // 使用异步方式更新设备状态，避免阻塞WebSocket关闭
                Thread {
                    try {
                        deviceService.updateDeviceStatus(deviceId, false, null)
                        logger.debug("设备状态更新成功: deviceId=$deviceId, online=false")
                    } catch (e: IllegalArgumentException) {
                        if (e.message == "设备不存在") {
                            logger.debug("连接关闭时设备不存在，忽略状态更新: deviceId=$deviceId")
                        } else {
                            logger.warn("更新设备状态失败: deviceId=$deviceId, error=${e.message}")
                        }
                    } catch (e: Exception) {
                        logger.warn("更新设备状态时发生未知错误: deviceId=$deviceId, error=${e.message}")
                    }
                }.start()
            } catch (e: Exception) {
                logger.warn("启动设备状态更新线程失败: deviceId=$deviceId, error=${e.message}")
            }
        }

        if (userId != null) {
            userSessions[userId]?.remove(session)
        }

        logger.info("WebSocket连接关闭: deviceId=$deviceId, userId=$userId, status=$status")
    }

    private fun handleTextPush(session: WebSocketSession, message: WebSocketMessageDto) {
        val data = message.data as Map<*, *>
        val targetDeviceId = data["targetDeviceId"] as String
        val content = data["content"] as String
        val sourceUserId = session.attributes["userId"] as String

        // 验证权限
        if (!deviceService.validateDeviceOwnership(targetDeviceId, sourceUserId)) {
            sendError(session, "权限不足")
            return
        }

        // 推送到目标设备
        val targetSession = deviceSessions[targetDeviceId]
        val isTargetOnline = targetSession != null && targetSession.isOpen
        
        // 使用设备状态管理器检查状态
        val isDeviceOnlineInDB = deviceStatusManager.isDeviceOnline(targetDeviceId)
        
        if (isTargetOnline) {
            sendMessage(
                targetSession, WebSocketMessageDto(
                    type = "text_received",
                    data = mapOf(
                        "content" to content,
                        "sourceDeviceId" to session.attributes["deviceId"],
                        "timestamp" to System.currentTimeMillis()
                    )
                )
            )

            // 确认推送成功
            sendMessage(
                session, WebSocketMessageDto(
                    type = "text_push_success",
                    data = mapOf("targetDeviceId" to targetDeviceId)
                )
            )
        } else {
            // 如果WebSocket连接不存在但数据库显示在线，更新数据库状态
            if (isDeviceOnlineInDB) {
                logger.warn("检测到状态不一致，更新设备状态为离线: $targetDeviceId")
                try {
                    deviceService.updateDeviceStatus(targetDeviceId, false, null)
                } catch (e: Exception) {
                    logger.error("更新设备状态失败: $targetDeviceId", e)
                }
            }
            
            sendError(session, "目标设备离线")
            logger.warn("目标设备离线: $targetDeviceId (WebSocket状态: $isTargetOnline, 数据库状态: $isDeviceOnlineInDB)")
        }
    }

    private fun handleMobileTextInput(session: WebSocketSession, message: WebSocketMessageDto) {
        val data = message.data as Map<*, *>
        val content = data["content"] as String
        val targetDeviceId = data["targetDeviceId"] as String
        val userId = session.attributes["userId"] as String
        val sourceDeviceId = session.attributes["deviceId"] as String

        logger.info("处理手机端文字输入: content=$content, targetDeviceId=$targetDeviceId, sourceDeviceId=$sourceDeviceId")

        // 验证权限 - 确保用户拥有目标设备
        if (!deviceService.validateDeviceOwnership(targetDeviceId, userId)) {
            sendError(session, "权限不足")
            return
        }

        // 推送到目标设备（插件端）
        val targetSession = deviceSessions[targetDeviceId]
        val isTargetOnline = targetSession != null && targetSession.isOpen
        
        // 使用设备状态管理器检查状态
        val isDeviceOnlineInDB = deviceStatusManager.isDeviceOnline(targetDeviceId)
        
        if (isTargetOnline) {
            sendMessage(
                targetSession, WebSocketMessageDto(
                    type = "text_input",
                    data = mapOf(
                        "text" to content,
                        "source" to "mobile",
                        "sourceDeviceId" to sourceDeviceId,
                        "timestamp" to System.currentTimeMillis()
                    )
                )
            )

            // 确认推送成功
            sendMessage(
                session, WebSocketMessageDto(
                    type = "mobile_text_input_success",
                    data = mapOf("targetDeviceId" to targetDeviceId)
                )
            )

            logger.info("手机端文字已推送到目标设备: $targetDeviceId")
        } else {
            // 如果WebSocket连接不存在但数据库显示在线，更新数据库状态
            if (isDeviceOnlineInDB) {
                logger.warn("检测到状态不一致，更新设备状态为离线: $targetDeviceId")
                try {
                    deviceService.updateDeviceStatus(targetDeviceId, false, null)
                } catch (e: Exception) {
                    logger.error("更新设备状态失败: $targetDeviceId", e)
                }
            }
            
            sendError(session, "目标设备离线")
            logger.warn("目标设备离线: $targetDeviceId (WebSocket状态: $isTargetOnline, 数据库状态: $isDeviceOnlineInDB)")
        }
    }

    private fun handleVoiceStream(session: WebSocketSession, message: WebSocketMessageDto) {
        val data = message.data as Map<*, *>
        val audioData = data["audioData"] as String
        val isFinal = data["isFinal"] as Boolean
        val userId = session.attributes["userId"] as String
        val deviceId = session.attributes["deviceId"] as String

        // 将Base64字符串转换为ByteArray
        val audioBytes = java.util.Base64.getDecoder().decode(audioData)

        // 处理语音流数据（实时模式）
        val recognizedText = voiceRecognitionService.processAudioStream(audioBytes, isRealTime = true)

        // 如果是最终结果，保存语音记录
        if (isFinal) {
            voiceRecognitionService.saveVoiceRecord(userId, deviceId, recognizedText, 0.9)
        }

        // 发送识别结果
        sendMessage(
            session, WebSocketMessageDto(
                type = "voice_recognition_result",
                data = mapOf(
                    "text" to recognizedText,
                    "isFinal" to isFinal,
                    "timestamp" to System.currentTimeMillis()
                )
            )
        )
    }

    private fun handleHeartbeat(session: WebSocketSession, message: WebSocketMessageDto) {
        val deviceId = session.attributes["deviceId"] as String
        deviceService.updateLastHeartbeat(deviceId)

        sendMessage(
            session, WebSocketMessageDto(
                type = "heartbeat_response",
                data = mapOf("timestamp" to System.currentTimeMillis())
            )
        )
    }

    private fun handleStatusRequest(session: WebSocketSession, message: WebSocketMessageDto) {
        val userId = session.attributes["userId"] as String
        val devices = deviceService.getUserDevices(userId)
        
        // 更新设备状态为实时WebSocket状态
        val updatedDevices = devices.map { deviceInfo ->
            val isWebSocketOnline = deviceStatusManager.isDeviceOnline(deviceInfo.deviceId)
            if (deviceInfo.online != isWebSocketOnline) {
                logger.debug("设备状态不一致，更新为实时状态: ${deviceInfo.deviceId} (数据库: ${deviceInfo.online}, WebSocket: $isWebSocketOnline)")
                try {
                    deviceService.updateDeviceStatus(deviceInfo.deviceId, isWebSocketOnline, null)
                } catch (e: Exception) {
                    logger.error("更新设备状态失败: ${deviceInfo.deviceId}", e)
                }
            }
            
            // 返回实时状态
            deviceInfo.copy(online = isWebSocketOnline)
        }

        sendMessage(
            session, WebSocketMessageDto(
                type = "device_status_response",
                data = mapOf("devices" to updatedDevices)
            )
        )
    }

    private fun sendMessage(session: WebSocketSession, message: WebSocketMessageDto) {
        try {
            val jsonMessage = objectMapper.writeValueAsString(message)
            session.sendMessage(TextMessage(jsonMessage))
        } catch (e: Exception) {
            logger.error("发送WebSocket消息失败", e)
        }
    }

    private fun sendError(session: WebSocketSession, error: String) {
        sendMessage(
            session, WebSocketMessageDto(
                type = "error",
                data = mapOf("message" to error)
            )
        )
    }

    private fun ensureDeviceExists(deviceId: String, userId: String?) {
        val (deviceType, deviceName) = when {
            deviceId.startsWith("mobile_") -> "mobile_app" to "手机设备"
            deviceId.startsWith("chrome_") -> "browser_extension" to "Chrome Extension"
            else -> "unknown" to "未知设备"
        }

        // 只有在用户已登录时才注册设备
        if (userId != null && userId.isNotBlank()) {
            logger.info("用户已登录，注册设备: deviceId=$deviceId, userId=$userId")
            deviceService.registerIfNecessary(deviceId, deviceType, deviceName, userId)
        } else {
            logger.info("用户未登录，跳过设备注册: deviceId=$deviceId")
            // 不注册设备，等待用户登录后再注册
        }
    }

    private fun handleAuth(session: WebSocketSession, message: WebSocketMessageDto) {
        try {
            val data = message.data as Map<*, *>
            val token = data["token"] as? String
            val deviceId = data["deviceId"] as? String

            if (token.isNullOrBlank() || deviceId.isNullOrBlank()) {
                logger.warn("认证消息缺少必要参数: token=${token?.take(10)}..., deviceId=$deviceId")
                sendError(session, "认证消息格式错误")
                return
            }

            // 验证JWT token并提取用户信息
            // 这里可以添加JWT验证逻辑
            // 暂时记录认证信息
            logger.info("收到认证消息: deviceId=$deviceId, token=${token.take(10)}...")
            
            // 发送认证成功响应
            sendMessage(
                session, WebSocketMessageDto(
                    type = "auth_success",
                    data = mapOf(
                        "message" to "认证成功",
                        "timestamp" to System.currentTimeMillis()
                    )
                )
            )
            
            logger.info("WebSocket认证成功: deviceId=$deviceId")
            
        } catch (e: Exception) {
            logger.error("处理认证消息时出错", e)
            sendError(session, "认证处理失败")
        }
    }
    
    private fun handleConnectionConfirm(session: WebSocketSession, message: WebSocketMessageDto) {
        // 处理连接确认消息
        logger.debug("处理连接确认消息: ${message.data}")
        sendMessage(
            session, WebSocketMessageDto(
                type = "connection_confirm_response",
                data = mapOf(
                    "status" to "success",
                    "timestamp" to System.currentTimeMillis()
                )
            )
        )
    }
    
    /**
     * 检查设备是否在线（WebSocket连接状态）
     */
    fun isDeviceOnline(deviceId: String): Boolean {
        val session = deviceSessions[deviceId]
        val isOnline = session != null && session.isOpen
        // 同步状态到设备状态管理器
        deviceStatusManager.updateWebSocketStatus(deviceId, isOnline)
        return isOnline
    }
    
    /**
     * 发送文本到指定设备
     */
    fun sendTextToDevice(deviceId: String, text: String) {
        try {
            val session = deviceSessions[deviceId]
            if (session != null && session.isOpen) {
                val message = WebSocketMessageDto(
                    type = "text_input",
                    data = mapOf(
                        "content" to text,
                        "source" to "voice_recognition",
                        "timestamp" to System.currentTimeMillis()
                    )
                )
                sendMessage(session, message)
                logger.info("发送文本到设备成功: deviceId=$deviceId, text=$text")
            } else {
                logger.warn("设备不在线或连接已关闭: deviceId=$deviceId")
            }
        } catch (e: Exception) {
            logger.error("发送文本到设备失败: deviceId=$deviceId", e)
        }
    }
    
    /**
     * 发送语音识别结果到指定设备
     */
    fun sendVoiceRecognitionResult(deviceId: String, text: String, isFinal: Boolean) {
        try {
            val session = deviceSessions[deviceId]
            if (session != null && session.isOpen) {
                val message = WebSocketMessageDto(
                    type = "voice_recognition_result",
                    data = mapOf(
                        "text" to text,
                        "isFinal" to isFinal,
                        "timestamp" to System.currentTimeMillis()
                    )
                )
                sendMessage(session, message)
                logger.info("语音识别结果已发送到设备: deviceId=$deviceId, isFinal=$isFinal, text=${text.take(50)}...")
            } else {
                logger.warn("设备不在线或连接已关闭: deviceId=$deviceId")
            }
        } catch (e: Exception) {
            logger.error("发送语音识别结果到设备失败: deviceId=$deviceId", e)
        }
    }
} 